home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / Tool Chest / Development Platforms / AppsToGo / AppsToGo.src / DTS.StyleChat / Window.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-18  |  29.7 KB  |  939 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Window.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1990-1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* This file contains the code for the document procedure pointers for the main Wannabe
  20. ** document.  Wannabe currently only supports one type of documents, type 'DUMD',
  21. ** which stands for "DUMb Document". */
  22.  
  23. /* For more information on this file, please read the read.me file "=How to write your app". */ 
  24.  
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29.  
  30.  
  31. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  32. #include "App.defs.h"        /* Get various application definitions.            */
  33. #include "App.protos.h"        /* Get the prototypes for application.            */
  34.  
  35. #ifndef __ERRORS__
  36. #include <Errors.h>
  37. #endif
  38.  
  39. #ifndef __FONTS__
  40. #include <Fonts.h>
  41. #endif
  42.  
  43. #ifndef __RESOURCES__
  44. #include <Resources.h>
  45. #endif
  46.  
  47. #ifndef __TOOLUTILS__
  48. #include <ToolUtils.h>
  49. #endif
  50.  
  51. #ifndef __UTILITIES__
  52. #include "Utilities.h"
  53. #endif
  54.  
  55.  
  56.  
  57. /*****************************************************************************/
  58.  
  59.  
  60.  
  61. Boolean        gNoDefaultDocument = false;
  62.                     /* Set to true if app should boot with no default document. */
  63.                     /* This tells DTS.Lib..framework what you want. */
  64.  
  65. OSType        gAppWindowType = kDocFileType;    /* Main document type. */
  66. long        gAppWindowAttr = kwAppWindow;    /* Main window attributes. */
  67.  
  68. short        gMinVersion    = kMinVersion;    /* Minimum document version app can support. */
  69. short        gMaxVersion    = kMaxVersion;    /* Maximum document version app can support. */
  70.                                             /* More informing DTS.Lib..framework. */
  71.  
  72. extern short        gPrintPage;                /* Non-zero means we are printing. */
  73.                                             /* DTS.Lib..framework global. */
  74.  
  75. extern RgnHandle    gCursorRgn;                /* We handle cursors here, so we need */
  76. extern CursPtr        gCursorPtr;                /* to know about these things. */
  77.                                             /* Above are DTS.Lib..framework globals. */
  78.  
  79. /* Currently Wannabe doesn't ever change the cursor, so we don't actually need
  80. ** these referenced here.  However, since Wannabe is supposed to be an application
  81. ** in progress, it is very likely that you will need to reference these as your
  82. ** project develops.  See DTS.Chat and DTS.Draw for examples of setting the cursor. */
  83.  
  84. /* Some cursors are pointer-based, and some cursors are resource-based.
  85. ** If a cursor is resource-based, it needs to be loaded and made to not move,
  86. ** and then gCursorPtr can be set to point to it.  This makes all cursors
  87. ** pointer-based.  Also, gCursorPtr is used by DTS.Lib..framework to
  88. ** determine if there is a current cursor.  If gCursorPtr is nil, then
  89. ** there is no current cursor, and the cursor has to be recalculated, no
  90. ** matter where the mouse is.  If gCursorPtr is not nil, then if the
  91. ** mouse position is within the cursor region gCursorRgn, the cursor is
  92. ** correct, and no recalculation is necessary.  If it is outside this region,
  93. ** then it is recalculated.  What does this all mean?  It means that if you
  94. ** want to guarantee that the cursor is recalculated next time DoWindowCursor()
  95. ** is called, set gCursorPtr to nil.
  96. **
  97. ** If you have a cursor resource, you need to:
  98. ** 1) Load the resource.
  99. ** 2) Make a fixed copy of it.
  100. ** 3) Set the cursor to it.
  101. ** 4) Set gCursorPtr to point to the fixed copy.
  102. **
  103. ** There is a function that does almost all of this, called DoSetResCursor().
  104. ** It does all but set gCursorPtr to it.  (It actually sets gCursorPtr to nil.)
  105. ** It does return a pointer to the permanent copy, so typically what you will
  106. ** want to do is the following:
  107. **     gCursorPtr = DoSetResCursor(theCursorID);
  108. **
  109. ** So why set gCursorPtr to nil as the default action?  This allows you to
  110. ** set a temporary cursor, which will be replaced when DoWindowCursor() is
  111. ** called next, or it allows you to set a cursor that maps to the cursor
  112. ** region gCursorRgn (by setting gCursorPtr to the return result). */
  113.  
  114. static Boolean    GoFast(TEHandle teHndl, EventRecord *event);
  115.  
  116.  
  117.  
  118. /*****************************************************************************/
  119. /*****************************************************************************/
  120.  
  121.  
  122.  
  123. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  124.  
  125. /* Calculate application specific frame area (Called by DoCalcFrameRgn).
  126. ** You are passed an empty region.  You are supposed to add any custom frame
  127. ** parts that this document uses.  Typically there are no frame portions, as
  128. ** they are accounted for in other ways.  The scrollbars and grow icon will
  129. ** automatically be contributed to the calculation of the frame region.
  130. ** If you use sidebars, these are also added in automatically.  This is only
  131. ** used if the frame region is more complicated than can automatically be
  132. ** handled.  So, almost always, you will simply leave the region empty. */
  133.  
  134. #pragma segment TheDoc
  135. void    CalcFrameRgn(FileRecHndl frHndl, WindowPtr window, RgnHandle rgn)
  136. {
  137. #pragma unused (frHndl, window, rgn)
  138. }
  139.  
  140.  
  141.  
  142. /*****************************************************************************/
  143.  
  144.  
  145.  
  146. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  147.  
  148. /* This is called (by DoContentClick()) when a mouse-down event occurs in the content of
  149. ** a window.  Other applications might want to call FindControl, TEClick, etc., to
  150. ** further process the click. */
  151.  
  152. #pragma segment TheDoc
  153. void    ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  154. {
  155. #pragma unused (firstClick)
  156.  
  157.     ControlHandle    ctl;
  158.     short            action, cnum;
  159.  
  160.     cnum = IsCtlEvent(window, event, &ctl, &action);
  161.         /* That was easy.  Scrolling was just handled.  Other stuff would be handled
  162.         ** by IsCtlEvent, if we had other stuff to do.  We don't have any other
  163.         ** controls in the content besides the document scrollbars. */
  164.  
  165.     return;
  166. }
  167.  
  168.  
  169.  
  170. /*****************************************************************************/
  171.  
  172.  
  173.  
  174. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  175.  
  176. /* DoKeyDown() is first called by the application.  Then if the key isn't a menu
  177. ** key, DoKeyDown() calls this code.  Here are the rules for this function:
  178. **
  179. ** 1) If you handle the key, return(true).  This completes the key handling.
  180. ** 2) If you don't handle the key, you return false.  However, there are two
  181. **    situations for not handling the key:
  182. **      a) You want someone else to.
  183. **      b) You want nobody else to look at the key.
  184. **    This is what the boolean passThrough is for.  If you wish the next window
  185. **    to have a look at the key, set the boolean passThrough to true.  passThrough
  186. **    is already initialized to false, which is the common case, so you only have
  187. **    to worry about setting it true.
  188. **
  189. ** If you have a window that never processes keys and always passes them through,
  190. ** just set the contentKeyProc to nil.  This will indicate to the application
  191. ** framework that all keys should be passed through this window.  DTS.Draw has
  192. ** such a window.  Its palette window doesn't accept keys.  They are passed through
  193. ** to document windows. */
  194.  
  195. #pragma segment TheDoc
  196. Boolean    ContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  197. {
  198. #pragma unused (passThrough)
  199.  
  200.     short    ctlNum, action;
  201.  
  202.     if ((event->message & charCodeMask) == 0x03) {
  203.         SendMessage((FileRecHndl)GetWRefCon(window), kStylMssg);
  204.         SendMessage((FileRecHndl)GetWRefCon(window), kTextMssg);
  205.         return(true);
  206.     }
  207.     ctlNum = IsCtlEvent(window, event, nil, &action);
  208.     if (ctlNum == 1) {
  209.         if (action == 2) {
  210.             SetWindowDirty(window);
  211.             DoAdjustMenus();
  212.             return(true);
  213.         }
  214.     }
  215.  
  216.     return(false);
  217. }
  218.  
  219.  
  220.  
  221. /*****************************************************************************/
  222.  
  223.  
  224.  
  225. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  226.  
  227. /* Draw application specific content (Called by DoDrawFrame).
  228. **
  229. ** If your application has any custom frame areas, or if it uses sidebars,
  230. ** this is the function that you would put the frame drawing code.  The
  231. ** document scrollbars and grow icon drawing is handled by DTS.framework.
  232. ** Just do the sidebar and custom areas here. */
  233.  
  234. #pragma segment TheDoc
  235. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  236. {
  237.     MoveTo(0, (*frHndl)->fileState.topSidebar - 1);
  238.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, (*frHndl)->fileState.topSidebar - 1);
  239.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, 16383);
  240.  
  241.     BeginFrame(window);
  242.     DoDrawControls(window, activate);
  243.     EndFrame(window);
  244. }
  245.  
  246.  
  247.  
  248. /*****************************************************************************/
  249.  
  250.  
  251.  
  252. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  253.  
  254. /* Frees up any application-specific memory in the document.  This is called by
  255. ** DoFreeDocument, which is called by DisposeDocument().  The application would
  256. ** call DisposeDocument(), not DoFreeDocument() or FreeDocument() directly.
  257. **
  258. ** The document may have a bunch of handles off the main handle of the document.
  259. ** This is where they are freed.  DisposeDocument calls this prior to releasing
  260. ** the ram for the main handle of the document, so release everything else
  261. ** here, or you will have a memory leak.
  262. **
  263. ** NOTE:  Calling DefaultFreeDocument() frees up all memory used by a
  264. ** hierarchical document (see TreeObj package). */
  265.  
  266. #pragma segment TheDoc
  267. OSErr    FreeDocument(FileRecHndl frHndl)
  268. {
  269.     Handle            textHndl;
  270.     StScrpHandle    textStyl;
  271.  
  272.     if (textHndl = (*frHndl)->d.doc.textHndl) {
  273.         DisposHandle(textHndl);
  274.         (*frHndl)->d.doc.textHndl = nil;
  275.     }
  276.     if (textStyl = (*frHndl)->d.doc.textStyl) {
  277.         DisposHandle((Handle)textStyl);
  278.         (*frHndl)->d.doc.textStyl = nil;
  279.     }
  280.     DefaultFreeDocument(frHndl);
  281.  
  282.     return(noErr);
  283. }
  284.  
  285.  
  286.  
  287. /*****************************************************************************/
  288.  
  289.  
  290.  
  291. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  292.  
  293. /* Any additional window disposal tasks can be handled here. */
  294.  
  295. #pragma segment TheDoc
  296. OSErr    FreeWindow(FileRecHndl frHndl, WindowPtr window)
  297. {
  298. #pragma unused (window)
  299.  
  300.     SendMessage(frHndl, kDisconnectMssg);
  301.         /* Let the remote user know the window is closing.  We must handle this,
  302.         ** as DTS.Lib..framework doesn't.   it is application-specific. */
  303.  
  304.     return(noErr);
  305.         /* We always return noErr here, even if SendMessage returned an error.  Why?
  306.         ** Because we don't want the closing of the window to halt just because the
  307.         ** network connection may have been lost, which is about the only error that
  308.         ** can occur.  If there's something bogus about the window, we most likely
  309.         ** want to get rid of it, with good riddance. */
  310. }
  311.  
  312.  
  313.  
  314. /*****************************************************************************/
  315.  
  316.  
  317.  
  318. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  319.  
  320. /* Image the document into the current port.
  321. **
  322. ** The only thing tricky about this function is that it needs to key off of
  323. ** the global variable gPrintPage.  gPrintPage is the current page that is
  324. ** being printed.  If gPrintPage is 0, then you are drawing to the window.
  325. **
  326. ** For when printing:
  327. **
  328. ** If gPrintPage is non-0, that is the page to be printed.  If after imaging
  329. ** the page there are no more pages, you should set gPrintPage to 0.  This
  330. ** indicates to the print loop that the end of the document has been reached.
  331. ** Even if the user indicated in the job dialog to print more pages, setting
  332. ** gPrintPage to 0 states that the last page has been printed.  This is necessary
  333. ** because the print loop can't know when printing is done.  The imaging procedure
  334. ** is the logical one to state when everything has been imaged. */
  335.  
  336. #pragma segment TheDoc
  337. OSErr    ImageDocument(FileRecHndl frHndl)
  338. {
  339.     WindowPtr        thePort;
  340.     Rect            rct, theInk;
  341.     TEHandle        te;
  342.     short            pageCol;
  343.     OSErr            err;
  344.     static short    taskOffset, taskNum;
  345.  
  346.     err = noErr;
  347.     GetPort(&thePort);
  348.  
  349.     if (!gPrintPage) {                                        /* If not printing... */
  350.         DoDrawControls(thePort, false);                        /* Draw the content controls. */
  351.     }
  352.     else {
  353.  
  354.         if (gPrintPage == 1)
  355.             taskOffset = taskNum = 0;
  356.  
  357.         theInk = thePort->portRect;
  358.         InsetRect(&theInk, 4, 4);        /* Just so no characters get clipped. */
  359.  
  360.         pageCol = 0;
  361.         for (rct = theInk; taskNum < 2;) {
  362.             te = (taskNum) ? (*frHndl)->d.doc.outBox : (*frHndl)->d.doc.inBox;
  363.             if (err = CTEPrint(te, &taskOffset, &rct)) break;
  364.             if (taskOffset != -1) return(noErr);    /* Text went to bottom of page. */
  365.             taskOffset = 0;                            /* Done with this TextEdit record. */
  366.             ++taskNum;
  367.             rct.top    = rct.bottom + 20;
  368.             rct.bottom = theInk.bottom;
  369.             if (rct.top + 20 >= rct.bottom) return(noErr);
  370.                 /* No page left or not enough to bother with. */
  371.         }
  372.         gPrintPage = 0;
  373.     }
  374.  
  375.     return(err);
  376. }
  377.  
  378.  
  379.  
  380. /*****************************************************************************/
  381.  
  382.  
  383.  
  384. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  385.  
  386. /* This function does the remaining window initialization.
  387. **
  388. ** There may be additional content initialization for the window.  At this point,
  389. ** you have a window, but it is currently invisible.  If you return noErr, then
  390. ** the window will be set to the state indicated for that window.  Why this function?
  391. ** You may wish to add controls to the content of the window.  You may have a
  392. ** TextEdit record in the content.  All of these sort of things can't be created
  393. ** until there is a window to contain them.  First a document is read in, and then
  394. ** if the document creation succeeds, a window is created for that document.
  395. ** At this point we have a document, and we are on our way to having a window.
  396. ** All that remains is any additional content initialization.  Do it, return
  397. ** noErr, and everybody's happy.  If something goes wrong here, return the error,
  398. ** and the incomplete window will be disposed of. */
  399.  
  400. #pragma segment TheDoc
  401. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  402. {
  403.     OSErr            err;
  404.     WindowPtr        oldPort;
  405.     short            i, mode;
  406.     Rect            ctlRect, brdrRect, viewRect, destRect;
  407.     TEHandle        teHndl, textBox[2];
  408.     Handle            text;
  409.     StScrpHandle    styl;
  410.  
  411.     err = AddControlSet(window, (*frHndl)->fileState.sfType, kwStandardVis, 0, 0, nil);
  412.     if (err) return(err);
  413.  
  414.     GetPort(&oldPort);
  415.     SetPort(window);
  416.  
  417.     for (i = 0; i < 2; ++i) {
  418.         ctlRect = window->portRect;
  419.         --ctlRect.top;
  420.         --ctlRect.left;
  421.         ctlRect.right  -= 14;
  422.  
  423.         ctlRect.bottom = ctlRect.top + (ctlRect.bottom - ctlRect.top + 1) / 2;
  424.         if (i) {
  425.             ctlRect.top = ctlRect.bottom - 1;
  426.             ctlRect.bottom = window->portRect.bottom + 1;
  427.         }
  428.  
  429.         brdrRect = ctlRect;
  430.         viewRect = ctlRect;
  431.         InsetRect(&viewRect, 4, 4);
  432.         destRect = viewRect;
  433.         destRect.right -= 2;    /* This fixes a TextEdit problem where the
  434.                                 ** view has to be a little outside the dest on
  435.                                 ** the right, or else characters are clipped. */
  436.  
  437.         mode  = i ? (cteVScrollLessGrow | cteActive | cteStyledTE)
  438.                   : (cteReadOnly + cteVScroll | cteStyledTE);
  439.         CTENew(rTECtl,            /* View ctl ResID used for TextEdit control.       */
  440.                true,            /* Control visible.                                   */
  441.                window,            /* Window to hold TERecord.                           */
  442.                &teHndl,            /* Return handle for TERecord.                        */
  443.                &ctlRect,        /* Rect for view control.                           */
  444.                &destRect,        /* destRect for TERecord.                           */
  445.                &viewRect,        /* viewRect for TERecord.                           */
  446.                &brdrRect,        /* Used to frame a border.                           */
  447.                kMaxNumChars,    /* Maximum TextEdit document length.               */
  448.                mode                /* Read-only or read-write, vert scroll, grow box. */
  449.         );
  450.         if (!(textBox[i] = teHndl)) {
  451.             SetPort(oldPort);
  452.             return(memFullErr);
  453.         }
  454.     }
  455.  
  456.     (*frHndl)->d.doc.inBox  = textBox[0];
  457.     (*frHndl)->d.doc.outBox = textBox[1];
  458.  
  459.     CTESetFastKeys(textBox[1], GoFast);
  460.  
  461.     if (text = (*frHndl)->d.doc.textHndl) {
  462.         styl = (*frHndl)->d.doc.textStyl;
  463.         DisposeHandle(CTESwapText(textBox[1], text, styl, false));
  464.         (*frHndl)->d.doc.textHndl = nil;
  465.         if (styl) {
  466.             DisposeHandle((Handle)styl);
  467.             (*frHndl)->d.doc.textStyl = nil;
  468.         }
  469.     }
  470.  
  471.     SetPort(oldPort);
  472.     return(noErr);
  473. }
  474.  
  475. static Boolean    GoFast(TEHandle teHndl, EventRecord *event)
  476. {
  477. #pragma unused (teHndl)
  478.  
  479.     if (event->modifiers & cmdKey) return(false);
  480.     return(true);
  481. }
  482.  
  483.  
  484.  
  485. /*****************************************************************************/
  486.  
  487.  
  488.  
  489. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  490.  
  491. #pragma segment TheDoc
  492. OSErr    ReadDocument(FileRecHndl frHndl)
  493. {
  494.     short            fileRefNum, oldRes;
  495.     OSErr            err;
  496.     char            hstate;
  497.     long            count;
  498.     Handle            textHndl;
  499.     StScrpHandle    styl;
  500.  
  501.     fileRefNum = (*frHndl)->fileState.refNum;
  502.  
  503.     err = SetFPos(fileRefNum, fsFromStart, 0);
  504.         /* Set the file position to the beginning of the file. */
  505.  
  506.     if (!err)
  507.         err = DoReadDocumentHeader(frHndl);
  508.             /* Read the document header.
  509.             ** Note that this doesn't guarantee that we will actually do something.
  510.             ** Text documents don't have a header.  The read and write header procs have
  511.             ** been set to nil for text documents, so calling DoReadDocumentHeader
  512.             ** will do nothing for text documents.  DTS.StyleChat documents do have a header,
  513.             ** and by calling DoReadDocumentHeader, the header will be read in. */
  514.  
  515.     if (!err) {        /* Read TextEdit text from file. */
  516.         textHndl = NewHandle(kMaxNumChars);
  517.         if (!(err = MemError())) {
  518.             count = kMaxNumChars;
  519.                 /* The size of the text isn't saved to disk.  This is the maximum
  520.                 ** that a TextEdit record can accept. */
  521.             hstate = LockHandleHigh(textHndl);
  522.             err    = FSRead(fileRefNum, &count, *textHndl);
  523.             HSetState(textHndl, hstate);
  524.  
  525.             if (err == eofErr)
  526.                 err = noErr;
  527.  
  528.             if (err)
  529.                 count = 0;
  530.  
  531.             SetHandleSize(textHndl, count);
  532.                 /* Set the handle to the actual size of the text on disk. */
  533.             (*frHndl)->d.doc.textHndl = textHndl;
  534.         }
  535.     }
  536.  
  537.     if (!err) {
  538.         err = UseDocResFile(frHndl, &oldRes, fsRdWrPerm);
  539.         if (!err) {
  540.             if (styl = (StScrpHandle)GetResource(kStylResType, kStylResID))
  541.                 DetachResource((Handle)styl);
  542.             (*frHndl)->d.doc.textStyl = styl;
  543.         }
  544.         UseResFile(oldRes);
  545.     }
  546.  
  547.     return(err);
  548. }
  549.  
  550.  
  551.  
  552. /*****************************************************************************/
  553.  
  554.  
  555.  
  556. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  557.  
  558. /* Resize application specific content (Called by ResizeWindow).
  559. **
  560. ** This gets called when a user does a zoom or window resizing operation.
  561. ** It is possible that things in the content need to be resized in conjunction
  562. ** with the resizing of the window. */
  563.  
  564. #pragma segment TheDoc
  565. void    ResizeContent(WindowPtr window, short oldh, short oldv)
  566. {
  567. #pragma unused (oldh, oldv)
  568.  
  569.     FileRecHndl    frHndl;
  570.     WindowPtr    oldPort;
  571.     short        i;
  572.     Rect        rct;
  573.     TEHandle    te;
  574.  
  575.     if (!window) return;
  576.  
  577.     frHndl  = (FileRecHndl)GetWRefCon(window);
  578.     oldPort = SetFilePort(frHndl);
  579.  
  580.     for (i = 0; i < 2; ++i) {
  581.         rct = window->portRect;
  582.         --rct.top;
  583.         --rct.left;
  584.         rct.right  -= 14;
  585.  
  586.         rct.bottom = rct.top + (rct.bottom - rct.top + 1) / 2;
  587.         if (i) {
  588.             rct.top = rct.bottom - 1;
  589.             rct.bottom = window->portRect.bottom + 1;
  590.         }
  591.  
  592.         te = (i) ? (*frHndl)->d.doc.outBox : (*frHndl)->d.doc.inBox;
  593.         CTEMove(te, rct.left, rct.top);
  594.         CTESize(te, rct.right - rct.left, rct.bottom - rct.top, true);
  595.     }
  596.  
  597.     SetPort(oldPort);
  598. }
  599.  
  600.  
  601.  
  602. /*****************************************************************************/
  603.  
  604.  
  605.  
  606. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  607.  
  608. /* Scroll application specific frame (Called by DoScrollFrame).
  609. **
  610. ** Some applications may need to scroll the "frame" of the document along
  611. ** with the document contents.  This is common for applications with rulers,
  612. ** or other similar sidebar items. */
  613.  
  614. #pragma segment TheDoc
  615. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  616. {
  617. #pragma unused (frHndl, window, dh, dv)
  618. }
  619.  
  620.  
  621.  
  622. /*****************************************************************************/
  623.  
  624.  
  625.  
  626. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  627.  
  628. /* Since the hierarchical document package isn't used by DTS.Chat,
  629. ** this function actually never gets called. */
  630.  
  631. #pragma segment TheDoc
  632. void    UndoFixup(FileRecHndl frHndl, Point contOrg, Boolean afterUndo)
  633. {
  634. #pragma unused (frHndl, contOrg, afterUndo)
  635.  
  636.     /* See DTS.Draw for an example of what you might do here. */
  637. }
  638.  
  639.  
  640.  
  641. /*****************************************************************************/
  642.  
  643.  
  644.  
  645. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  646.  
  647. /* This function is where you adjust the cursor to reflect the location in the
  648. ** document or window.  You have the additional input of gCursorRgn to deal
  649. ** with.  The way that the cursor handling works is as follows:
  650. ** 1) The application calls DoWindowCursor().
  651. ** 2) DoWindowCursor() works its way through the windows/documents, front to back.
  652. **    It looks at the document's windowCursorProc and checks to see if the document
  653. **    has one.  If the document doesn't have one, then it assumes that that window
  654. **    always wants an arrow.  If the cursor is over that window, the cursor is set
  655. **    to an arrow, and we're done.  If the cursor isn't over the window, then the next
  656. **    window is tried.  If all documents don't have a windowCursorProc, then the cursor
  657. **    is set to an arrow (for the non-document area of the screen).
  658. ** 3) If a document has a windowCursorProc, then the proc is called.  The proc's
  659. **    job is as follows:
  660. **    a) If the cursor is over a position that is determined by the window, then
  661. **       the proc removes other areas from gCursorRgn.  Note that it should not
  662. **       simply set the area to what it "thinks" is the correct area.  This window
  663. **       may not be the front-most.  Other windows will have already been subtracted
  664. **       from gCursorRgn.  The resultant gCursorRgn is the correct cursor area,
  665. **       and should be passed to WaitNextEvent calls in the application (already the case
  666. **       in EventLoop.c).  Also, the cursor should be set to the correct cursor, of course.
  667. **       You should also return true, as the cursor has been determined.
  668. **    b) If the cursor is not over a position for this window, then you should
  669. **       return.  You will either pass back true or false.  If you don't wish
  670. **       windows behind this window to have a shot at cursor determination, then
  671. **       return true.  This states that the cursor is "determined".  It is, in the
  672. **       sense that no further determination will occur.  If you return false, then
  673. **       other windows get a shot at determining the cursor.
  674. **
  675. ** Setting the cursor to the correct cursor isn't as easy as you would expect.
  676. ** DTS.Lib..framework uses the global gCursorPtr as the reference to the cursor.  This is
  677. ** fine if the cursor is pointer-based, but if the cursor is resource-based, it is a bit
  678. ** more of a problem.  What you will need to do is to call DoSetResCursor() to make the
  679. ** resource cursor pointer-based.  DoSetResCursor() will set gCursorPtr to nil, and it
  680. ** also returns the pointer to the permanent copy of the cursor resource.  Just set gCursorPtr
  681. ** to the return result of DoSetResCursor(), and you will be set. */
  682.  
  683. #pragma segment TheDoc
  684. Boolean    WindowCursor(FileRecHndl frHndl, WindowPtr window, Point globalPt)
  685. {
  686.     WindowPtr        oldPort;
  687.     RgnHandle        contRgn;
  688.     Rect            outBoxRct, teViewRct, contRct;
  689.     TEHandle        outBox, teHndl;
  690.     ControlHandle    viewCtl;
  691.     Point            contOrg;
  692.  
  693.     /* For the DTS.StyleChat sample, we display an i-beam cursor when over the outbox. */
  694.  
  695.     CopyRgn(((WindowPeek)window)->contRgn, (contRgn = NewRgn()));
  696.     viewCtl   = CTEViewFromTE(outBox = (*frHndl)->d.doc.outBox);
  697.     outBoxRct = (*viewCtl)->contrlRect;        /* Local coordinates of outbox. */
  698.     InsetRect(&outBoxRct, 2, 2);
  699.     GetContentOrigin(window, &contOrg);        /* Scroll position of window. */
  700.     OffsetRect(&outBoxRct, -contOrg.h, -contOrg.v);
  701.         /* By offseting the outbox rect by the amount scrolled, we now have
  702.         ** the local position of the outbox within the potentially scrolled
  703.         ** window content. */
  704.     GetContentRect(window, &contRct);
  705.         /* This returns the content portion of the window in local coordinates,
  706.         ** less document scrollbar area. */
  707.     SectRect(&outBoxRct, &contRct, &outBoxRct);
  708.         /* Part of the outbox still visible after scrolling. */
  709.  
  710.     GetPort(&oldPort);
  711.     SetPort(window);
  712.     LocalToGlobalRect(&outBoxRct);
  713.     SetPort(oldPort);
  714.         /* The outbox rect, in global coordinates. */
  715.  
  716.     if (CTETargetInfo(&teHndl, &teViewRct) == window) {
  717.         /* If target TextEdit control belongs to this window... */
  718.  
  719.         if ((teHndl == outBox) && (PtInRect(globalPt, &outBoxRct))) {
  720.             /* If target TextEdit control is the outbox, and the cursor
  721.             ** is over the visible part of the outbox... */
  722.  
  723.             gCursorPtr = DoSetResCursor(ibeamCursor);
  724.             RectRgn(contRgn, &outBoxRct);
  725.             SectRgn(gCursorRgn, contRgn, gCursorRgn);
  726.             DisposeRgn(contRgn);
  727.             return(true);
  728.         }
  729.     }
  730.  
  731.     DiffRgn(((WindowPeek)window)->strucRgn, contRgn, contRgn);
  732.     SectRgn(gCursorRgn, contRgn, gCursorRgn);
  733.     DisposeRgn(contRgn);
  734.     SetCursor(gCursorPtr = &qd.arrow);
  735.     return(true);
  736. }
  737.  
  738.  
  739.  
  740. /*****************************************************************************/
  741.  
  742.  
  743.  
  744. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  745.  
  746. /* After the DTS.Lib framework disposes of a window, it calls here.  This is
  747. ** to give the application a chance to do any additional tasks related to
  748. ** a window closing.  DTS.Chat doesn't have anything else extra to do. */
  749.  
  750. #pragma segment TheDoc
  751. void    WindowGoneFixup(WindowPtr window)
  752. {
  753. #pragma unused (window)
  754. }
  755.  
  756.  
  757.  
  758. /*****************************************************************************/
  759.  
  760.  
  761.  
  762. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  763.  
  764. /* The reverse function of ReadDocument. */
  765.  
  766. #pragma segment TheDoc
  767. OSErr    WriteDocument(FileRecHndl frHndl)
  768. {
  769.     short            fileRefNum, oldRes;
  770.     OSErr            err;
  771.     char            hstate;
  772.     long            count, fpos;
  773.     TEHandle        te;
  774.     Handle            textHndl, oldr;
  775.     StScrpHandle    styl;
  776.  
  777.     fileRefNum = (*frHndl)->fileState.refNum;
  778.  
  779.     err = SetFPos(fileRefNum, fsFromStart, 0);
  780.         /* Set the file position to the beginning of the file. */
  781.  
  782.     if (!err)
  783.         err = DoWriteDocumentHeader(frHndl);
  784.             /* Write the document header.
  785.             ** Note that this doesn't guarantee that we will actually do something.
  786.             ** Text documents don't have a header.  The read and write header procs have
  787.             ** been set to nil for text documents, so calling DoWriteDocumentHeader
  788.             ** will do nothing for text documents.  DTS.StyleChat documents do have a header,
  789.             ** and by calling DoWriteDocumentHeader, the header will be written out. */
  790.  
  791.     if (!err) {        /* Write out-box TextEdit control text to file. */
  792.         te       = (*frHndl)->d.doc.outBox;
  793.         textHndl = (*te)->hText;
  794.         count    = (*te)->teLength;
  795.         hstate   = LockHandleHigh(textHndl);
  796.         err      = FSWrite(fileRefNum, &count, *textHndl);
  797.         HSetState(textHndl, hstate);
  798.     }
  799.  
  800.     if (!err) {
  801.         err = GetFPos(fileRefNum, &fpos);
  802.         if (!err)
  803.             err = SetEOF(fileRefNum, fpos);
  804.     }            /* The document may be shorter than last time it was written to disk.
  805.                 ** Handle this case by ending the file based on the new length. */
  806.  
  807.     if (!err) {
  808.         styl = CTEGetFullStylScrap((*frHndl)->d.doc.outBox);
  809.         if (styl) {
  810.             err = UseDocResFile(frHndl, &oldRes, fsRdWrPerm);
  811.             if (!err) {
  812.                 if (oldr = GetResource(kStylResType, kStylResID)) RmveResource(oldr);
  813.                 AddResource((Handle)styl, kStylResType, kStylResID, nil);
  814.                 WriteResource((Handle)styl);
  815.                 DetachResource((Handle)styl);
  816.                 UseResFile(oldRes);
  817.                 DisposeHandle((Handle)styl);
  818.             }
  819.         }
  820.     }
  821.  
  822.     return(err);
  823. }
  824.  
  825.  
  826.  
  827. /*****************************************************************************/
  828.  
  829.  
  830.  
  831. /* •• You don't call this.  DTS.Lib..framework does at open-application time. •• */
  832.  
  833. #pragma segment TheDoc
  834. OSErr    DoOpenApplication(void)
  835. {
  836.     MenuHandle    menu;
  837.  
  838.     if (menu = GetMHandle(mFonts))
  839.         AddResMenu(menu, 'FONT');
  840.  
  841.     return(noErr);
  842. }
  843.  
  844.  
  845.  
  846. /*****************************************************************************/
  847. /*****************************************************************************/
  848. /*****************************************************************************/
  849.  
  850.  
  851.  
  852. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  853.  
  854. #pragma segment TheDoc
  855. Boolean    AdjustMenuItems(WindowPtr window, short menuID)
  856. {
  857.     Boolean        redrawMenuBar;
  858.     MenuHandle    menu;
  859.  
  860.     redrawMenuBar = false;
  861.  
  862.     switch (menuID) {
  863.         case mFile:
  864.             redrawMenuBar = DoAdjustFileMenu(window);
  865.             break;
  866.         case mEdit:
  867.             redrawMenuBar = DoAdjustEditMenu(window);
  868.             break;
  869.         case mCommunicate:
  870.             redrawMenuBar = DoAdjustCommunicateMenu(window);
  871.             break;
  872.         case mFonts:
  873.             redrawMenuBar = DoAdjustFontsMenu(window);
  874.             break;
  875.         default:
  876.             if (menu = GetMHandle(menuID))
  877.                 (*menu)->enableFlags |= 0xFFFFFFFEL;
  878.             break;
  879.     }
  880.  
  881.     return(redrawMenuBar);
  882. }
  883.  
  884.  
  885.  
  886. /*****************************************************************************/
  887.  
  888.  
  889.  
  890. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  891.  
  892. #pragma segment TheDoc
  893. Boolean    DoMenuItem(WindowPtr window, short menuID, short menuItem)
  894. {
  895. #pragma unused (window)
  896.  
  897.     return(DoMenuCommand(menuID, menuItem));
  898. }
  899.  
  900.  
  901.  
  902. /*****************************************************************************/
  903. /*****************************************************************************/
  904. /*****************************************************************************/
  905.  
  906.  
  907.  
  908. #pragma segment TheDoc
  909. OSErr    DuplicateDocument(FileRecHndl oldFrHndl, FileRecHndl *newFrHndl)
  910. {
  911.     OSErr        err;
  912.     TEHandle    te;
  913.     Handle        oldHndl, newHndl;
  914.     long        size;
  915.  
  916.     err = NewDocument(newFrHndl, (*oldFrHndl)->fileState.sfType, true);
  917.         /* Create a document and root object to copy the file data into. */
  918.  
  919.     if (err) return(err);
  920.  
  921.     te      = (*oldFrHndl)->d.doc.outBox;
  922.     oldHndl = (*te)->hText;
  923.     size    = (*te)->teLength;
  924.     if (newHndl = NewHandle(size)) {
  925.         (**newFrHndl)->d.doc.textHndl = newHndl;
  926.         BlockMove(*oldHndl, *newHndl, size);
  927.     }
  928.     else {
  929.         err = MemError();
  930.         DisposeDocument(*newFrHndl);
  931.         *newFrHndl = nil;
  932.     }
  933.  
  934.     return(err);
  935. }
  936.  
  937.  
  938.  
  939.